home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / kernel / imf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-02  |  20.0 KB  |  868 lines  |  [TEXT/KAHL]

  1. /* Interpretation of generic GDL images for Xconq.
  2.    Copyright (C) 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* Note!  This file does not use the standard "conq.h" header, so can't assume
  10.    all the usual definitions. */
  11.  
  12. #include "config.h"
  13. #include "misc.h"
  14. #include "lisp.h"
  15. #include "imf.h"
  16. ImageFile *get_image_file PROTO ((char *name));
  17.  
  18. #define MAXIMAGEFAMILIES 1000
  19.  
  20. #define MAXIMAGECOLORS 1000
  21.  
  22. static void write_pixmap PROTO ((FILE *fp, int w, int h, int aw, int ah,
  23.                  int pixelsize, int rowbytes,
  24.                  Obj *palette, int *rawpalette, int numcolors,
  25.                  Obj *lispdata, char *rawdata));
  26. static void write_bitmap PROTO ((FILE *fp, char *subtyp, int w, int h,
  27.                  Obj *data, char *rawdata));
  28. static void write_palette_contents PROTO ((FILE *fp, Obj *palette));
  29.  
  30. /* This is the array of known image families. */
  31.  
  32. ImageFamily **images;
  33.  
  34. /* This is the count of known image families. */
  35.  
  36. int numimages = 0;
  37.  
  38. ImageColor **colors;
  39.  
  40. int numcolors = 0;
  41.  
  42. ImageFile *image_files;
  43.  
  44. /* Create and return an image family. */
  45.  
  46. ImageFamily *
  47. new_imf(name)
  48. char *name;
  49. {
  50.     ImageFamily *imf;
  51.  
  52.     imf = (ImageFamily *) xmalloc(sizeof(ImageFamily));
  53.     imf->name = name;
  54.     return imf;
  55. }
  56.  
  57. ImageFamily *
  58. clone_imf(imf)
  59. ImageFamily *imf;
  60. {
  61.     Image *img, *img2, *truenext;
  62.     ImageFamily *imf2;
  63.  
  64.     imf2 = new_imf(imf->name);
  65.     memcpy(imf2, imf, sizeof(ImageFamily));
  66.     imf2->images = NULL;
  67.     /* Clone the images. */
  68.     for (img = imf->images; img != NULL; img = img->next) {
  69.     img2 = get_img(imf2, img->w, img->h);
  70.     truenext = img2->next;
  71.     memcpy(img2, img, sizeof(Image));
  72.     /* Clear the hook, we expect that the caller of this routine
  73.        will supply any new hook that might be necessary. */
  74.     img2->hook = NULL;
  75.     /* Restore the link. */
  76.     img2->next = truenext;
  77.     /* Note that pointers to raw image data and suchlike can be
  78.        left as-is, since they should be shared by image clones. */
  79.     }
  80.     return imf2;
  81. }
  82.  
  83. /* Test that the given name is a valid image family name (all alphanumeric,
  84.    hyphens anywhere but as first char). */
  85.  
  86. int
  87. valid_imf_name(name)
  88. char *name;
  89. {
  90.     char *tmp;
  91.  
  92.     for (tmp = name; *tmp; ++tmp) {
  93.     if (!(isalnum(*tmp)
  94.           || (tmp != name && *tmp == '-')))
  95.       return FALSE;
  96.     }
  97.     return TRUE;
  98. }
  99.  
  100. /* Given a name, find or create an image family with that name. */
  101.  
  102. ImageFamily *
  103. get_imf(name)
  104. char *name;
  105. {
  106.     ImageFamily *imf = NULL;
  107.     
  108.     if (name == NULL) {
  109.     init_warning("can't get an unnamed imf");
  110.     return NULL;
  111.     }
  112.     if (!valid_imf_name(name)) {
  113.     init_warning("\"%s\" is not a valid imf name", name);
  114.     return NULL;
  115.     }
  116.     if (images == NULL) {
  117.     images =
  118.       (ImageFamily **) xmalloc(MAXIMAGEFAMILIES * sizeof(ImageFamily *));
  119.     }
  120.     imf = find_imf(name);
  121.     if (imf == NULL) {
  122.     if (numimages >= MAXIMAGEFAMILIES) {
  123.         return NULL;
  124.     }
  125.     imf = new_imf(name);
  126.     if (imf != NULL) {
  127.         images[numimages++] = imf;
  128.     }
  129.     }
  130.     return imf;
  131. }
  132.  
  133. ImageFile *
  134. get_image_file(name)
  135. char *name;
  136. {
  137.     ImageFile *imfile;
  138.     
  139.     if (name == NULL)
  140.       run_error("can't get an unnamed imfile");
  141.     for (imfile = image_files; imfile != NULL; imfile = imfile->next) {
  142.     if (strcmp(name, imfile->name) == 0)
  143.       return imfile;
  144.     }
  145.     imfile = (ImageFile *) xmalloc(sizeof(ImageFile));
  146.     imfile->name = copy_string(name);
  147.     imfile->next = image_files;
  148.     image_files = imfile;
  149.     return imfile;
  150. }
  151.  
  152. void
  153. load_image_families(fp, loadnow, callback)
  154. FILE *fp;
  155. int loadnow;
  156. void (*callback) PROTO ((ImageFamily *imf, int loadnow));
  157. {
  158.     int done = FALSE, rslt, first = TRUE;
  159.     char buf1[80], buf2[80];
  160.     ImageFamily *imf;
  161.     ImageFile *imfile;
  162.     
  163.     while (!done) {
  164.     rslt = fscanf(fp, "%s %s\n", buf1, buf2);
  165.     if (rslt != 2)
  166.       break;
  167.     else if (strcmp(buf1, ".") == 0
  168.          && strcmp(buf2, ".") == 0)
  169.       done = TRUE;
  170.     else if (first) {
  171.         if (strcmp(buf1, "ImageFamilyName") == 0
  172.         && strcmp(buf2, "FileName") == 0)
  173.           first = FALSE;
  174.         else {
  175.         init_warning("File not a valid imf dir, will close and ignore");
  176.         /* We've already given a warning message, so pretend we're done
  177.            so the format error message doesn't get displayed below. */
  178.         done = TRUE;
  179.         break;
  180.         }
  181.     } else {
  182.         imf = get_imf(copy_string(buf1));
  183.         if (imf != NULL) {
  184.         imfile = get_image_file(buf2);
  185.         imf->location = imfile;
  186.         if (loadnow && !imfile->loaded) {
  187.             load_imf_file(imfile->name, callback);
  188.             imfile->loaded = TRUE;
  189.         } else {
  190.             if (callback != NULL)
  191.               (*callback)(imf, loadnow);
  192.         }
  193.         }
  194.     }
  195.     }
  196.     if (!done) {
  197.     init_warning("Format error in imf dir near %s, will only use part",
  198.              (imf ? imf->name : "???"));
  199.     }
  200. }
  201.  
  202. /* Given a filename, open it and read/interpret all the image-related
  203.    forms therein. */
  204.  
  205. int
  206. load_imf_file(filename, callback)
  207. char *filename;
  208. void (*callback) PROTO ((ImageFamily *imf, int loadnow));
  209. {
  210.     int startlineno = 1, endlineno = 1;
  211.     Obj *form;
  212.     FILE *fp;
  213.  
  214.     fp = fopen(filename, "r");
  215.     if (fp != NULL) {
  216.     /* Read everything in the file. */
  217.     while ((form = read_form(fp, &startlineno, &endlineno)) != lispeof) {
  218.         interp_imf_form(form, callback);
  219.     }
  220.     fclose(fp);
  221.     return TRUE;
  222.     }
  223.     return FALSE;
  224. }
  225.  
  226. /* Interpret a form, looking specifically for image-related forms. */
  227.  
  228. void
  229. interp_imf_form(form, imf_callback)
  230. Obj *form;
  231. void (*imf_callback) PROTO ((ImageFamily *imf, int loadnow));
  232. {
  233.     Obj *head;
  234.     ImageFamily *imf;
  235.     ImageColor *imc;
  236.  
  237.     head = car(form);
  238.     if (match_keyword(head, K_IMF)) {
  239.     imf = interp_imf(form);
  240.     if (imf_callback != NULL && imf != NULL)
  241.       (*imf_callback)(imf, TRUE);
  242.     } else if (match_keyword(head, K_PALETTE)) {
  243.     /* (should eventually be able to interpret shared palettes) */
  244.     } else if (match_keyword(head, K_COLOR)) {
  245.     interp_color(form);
  246.     } else {
  247.     /* Ignore any non-image forms, we might be reading from a 
  248.        normal game design. */
  249.     }
  250. }
  251.  
  252. /* Find the image family of the given name, if it exists. */
  253.  
  254. ImageFamily *
  255. find_imf(name)
  256. char *name;
  257. {
  258.     int i;
  259.  
  260.     for (i = 0; i < numimages; ++i) {
  261.     if (strcmp(name, images[i]->name) == 0)
  262.       return images[i];
  263.     }
  264.     return NULL;
  265. }
  266.  
  267. /* Get an image of the given size from the family, creating a new one
  268.    if necessary. */
  269.  
  270. Image *
  271. get_img(imf, w, h)
  272. ImageFamily *imf;
  273. int w, h;
  274. {
  275.     Image *img, *previmg = NULL;
  276.  
  277.     for (img = imf->images; img != NULL; img = img->next) {
  278.     if (w == img->w && h == img->h)
  279.       return img;
  280.     previmg = img;
  281.     }
  282.     /* Not found; create a new image and add it to the family. */
  283.     img = (Image *) xmalloc(sizeof(Image));
  284.     img->w = w;  img->h = h;
  285.     /* Default min/max limits to actual size. */
  286.     img->minw = img->maxw = w;  img->minh = img->maxh = h;
  287.     img->embedx = img->embedy = -1;
  288.     img->monodata = img->colrdata = img->maskdata = lispnil;
  289.     img->palette = lispnil;
  290.     /* Rely on zeroing of xmalloc blocks to avoid clearing other fields. */
  291.     /* Link at front of list of images. */
  292.     if (previmg != NULL)
  293.       previmg->next = img;
  294.     else
  295.       imf->images = img;
  296.     ++(imf->numsizes);
  297.     return img;
  298. }
  299.  
  300. Image *
  301. find_img(imf, w, h)
  302. ImageFamily *imf;
  303. int w, h;
  304. {
  305.     Image *img;
  306.     
  307.     for (img = imf->images; img != NULL; img = img->next) {
  308.     if (w == img->w && h == img->h)
  309.       return img;
  310.     }
  311.     return NULL;
  312. }
  313.  
  314. ImageFamily *
  315. interp_imf(form)
  316. Obj *form;
  317. {
  318.     ImageFamily *imf;
  319.  
  320.     if (stringp(cadr(form))) {
  321.     imf = get_imf(c_string(cadr(form)));
  322.     if (imf != NULL) {
  323.         interp_imf_contents(imf, cddr(form));
  324.     }
  325.     return imf;
  326.     } else {
  327.     /* garbage form */
  328.     }
  329.     return NULL;
  330. }
  331.  
  332. void
  333. interp_imf_contents(imf, clauses)
  334. ImageFamily *imf;
  335. Obj *clauses;
  336. {
  337.     Obj *rest, *clause;
  338.  
  339.     for (rest = clauses; rest != lispnil; rest = cdr(rest)) {
  340.     clause = car(rest);
  341.     if (consp(clause)) {
  342.         interp_image(imf, car(clause), cdr(clause));
  343.     } else {
  344.         /* garbage? */
  345.     }
  346.     }
  347. }
  348.  
  349. void
  350. interp_image(imf, size, parts)
  351. ImageFamily *imf;
  352. Obj *size, *parts;
  353. {
  354.     int w, h, imtype;
  355.     Image *img;
  356.     Obj *head, *rest, *typ, *prop, *proptype, *datalist;
  357.     
  358.     w = c_number(car(size));  h = c_number(cadr(size));
  359.     img = get_img(imf, w, h);
  360.     if (img == NULL)
  361.       run_error("no image?");
  362.     if (match_keyword(car(cddr(size)), K_TILE))
  363.       img->istile = TRUE;
  364.     img->actualw = w;  img->actualh = h;
  365.     img->pixelsize = img->rowbytes = 0;
  366.     img->palette = lispnil;
  367.     for (rest = parts; rest != lispnil; rest = cdr(rest)) {
  368.     head = car(rest);
  369.     typ = car(head);
  370.     imtype = K_OTHER_;
  371.     if (match_keyword(typ, K_MONO)) {
  372.         imtype = K_MONO_;
  373.     } else if (match_keyword(typ, K_MASK)) {
  374.         imtype = K_MASK_;
  375.     } else if (match_keyword(typ, K_COLOR)) {
  376.         imtype = K_COLR_;
  377.     } else if (match_keyword(typ, K_EMBED)) {
  378.         img->embedname = c_string(cadr(head));
  379.     } else if (match_keyword(typ, K_EMBED_AT)) {
  380.         img->embedx = c_number(cadr(head));
  381.         img->embedy = c_number(caddr(head));
  382.     } else {
  383.         run_warning("unknown image property");
  384.     }
  385.     if (imtype == K_OTHER_)
  386.       continue;
  387.     datalist = cdr(head);
  388.     /* Interpret random image subproperties. */
  389.     while (consp(car(datalist))) {
  390.         prop = car(datalist);
  391.         proptype = car(prop);
  392.         if (match_keyword(proptype, K_ACTUAL)) {
  393.         img->actualw = c_number(cadr(prop));
  394.         img->actualh = c_number(caddr(prop));
  395.         } else if (match_keyword(proptype, K_PIXEL_SIZE)) {
  396.         img->pixelsize = c_number(cadr(prop));
  397.         } else if (match_keyword(proptype, K_ROW_BYTES)) {
  398.         img->rowbytes = c_number(cadr(prop));
  399.         } else if (match_keyword(proptype, K_PALETTE)) {
  400.         img->palette = cdr(prop);
  401.         } else {
  402.         /* (should warn?) */
  403.         }
  404.         datalist = cdr(datalist);
  405.     }
  406.     switch (imtype) {
  407.       case K_MONO_:
  408.         img->monodata = datalist;
  409.         break;
  410.       case K_COLR_:
  411.         img->colrdata = datalist;
  412.         break;
  413.       case K_MASK_:
  414.         img->maskdata = datalist;
  415.         break;
  416.       default:
  417.         break;
  418.     }
  419.     }
  420.     /* Kind of a hack. */
  421.     img->minw = img->w / 4;  img->minh = img->h / 4;
  422.     img->maxw = img->w * 4;  img->maxh = img->h * 4;
  423. }
  424.  
  425. void
  426. interp_bytes(datalist, numbytes, destaddr, jump)
  427. Obj *datalist;
  428. int numbytes, jump;
  429. char *destaddr;
  430. {
  431.     int i, j = 0;
  432.     char *data = NULL;
  433.  
  434.     for (i = 0; i < numbytes; ++i) {
  435.     if (data == NULL || data[j] == '\0') {
  436.         if (!stringp(car(datalist)))
  437.           return; /* (should warn somehow?) */
  438.         data = c_string(car(datalist));
  439.         j = 0;
  440.         datalist = cdr(datalist);
  441.     }
  442.     /* Just skip over slashes, which are for appearance only. */
  443.     if (data[j] == '/')
  444.       ++j;
  445.     destaddr[i] = hextoi(data[j]) * 16 + hextoi(data[j+1]);
  446.     if (jump == 1 || (jump > 0 && i % jump == 0)) {
  447.         i += jump;
  448.         /* Be neat, put a zero in the location we're jumping over. */
  449.         /* (doesn't work for jump > 1, but that never happens anymore?) */
  450.         destaddr[i] = 0;
  451.     }
  452.     j += 2;
  453.     }
  454. }
  455.  
  456. void
  457. interp_palette(form)
  458. Obj *form;
  459. {
  460. }
  461.  
  462. ImageColor *
  463. interp_color(form)
  464. Obj *form;
  465. {
  466.     Obj *elts;
  467.     ImageColor *imc;
  468.  
  469.     if (stringp(cadr(form))) {
  470.     imc = get_imc(c_string(cadr(form)));
  471.     elts = cddr(form);
  472.     imc->r = c_number(car(elts));
  473.     imc->g = c_number(car(cdr(elts)));
  474.     imc->b = c_number(car(cddr(elts)));
  475.     return imc;
  476.     }
  477.     return NULL;
  478. }
  479.  
  480. ImageColor *
  481. new_image_color(name)
  482. char *name;
  483. {
  484.     ImageColor *imc;
  485.  
  486.     imc = (ImageColor *) xmalloc(sizeof(ImageColor));
  487.     imc->name = name;
  488.     return imc;
  489. }
  490.  
  491. char *canonical_color_name PROTO ((char *str));
  492.  
  493. char *
  494. canonical_color_name(str)
  495. char *str;
  496. {
  497.     return str;
  498. }
  499.  
  500. /* Given a name, find or create an image color with that name. */
  501.  
  502. ImageColor *
  503. get_imc(name)
  504. char *name;
  505. {
  506.     ImageColor *imc = NULL;
  507.  
  508.     if (name == NULL)
  509.       return NULL;
  510.     if (colors == NULL)
  511.       colors =
  512.     (ImageColor **) xmalloc(MAXIMAGECOLORS * sizeof(ImageColor *));
  513.     if ((imc = find_imc(name)) == NULL) {
  514.     if (numcolors >= MAXIMAGECOLORS)
  515.       return NULL;
  516.     imc = new_image_color(canonical_color_name(name));
  517.     if (imc != NULL) {
  518.         imc->r = imc->g = imc->b = 0;
  519.         colors[numcolors++] = imc;
  520.     }
  521.     }
  522.     return imc;
  523. }
  524.  
  525. static int colornamecmp PROTO ((char *str1, char *str2));
  526.  
  527. /* Find the image color of the given name, if it exists. */
  528.  
  529. ImageColor *
  530. find_imc(name)
  531. char *name;
  532. {
  533.     int i;
  534.  
  535.     for (i = 0; i < numcolors; ++i) {
  536.     if (colornamecmp(name, colors[i]->name) == 0)
  537.       return colors[i];
  538.     }
  539.     return NULL;
  540. }
  541.  
  542. /* X-style color names have several variants, but we only want one of them,
  543.    so this matches the variants with each other. */
  544.  
  545. static int
  546. colornamecmp(str1, str2)
  547. char *str1, *str2;
  548. {
  549.     while (*str1 != '\0' && *str2 != '\0') {
  550.     if (*str1 == *str2) {
  551.         ++str1;  ++str2;
  552.     } else if (isalpha(*str1) && isalpha(*str2)
  553.            && tolower(*str1) == tolower(*str2)) {
  554.         ++str1;  ++str2;
  555.     } else if (*str1 == 'a' && *str2 == 'e'
  556.            && *(str1+1) == 'y' && *(str2+1) == 'y') {
  557.         ++str1;  ++str2;
  558.     } else if (*str1 == 'e' && *str2 == 'a'
  559.            && *(str1+1) == 'y' && *(str2+1) == 'y') {
  560.         ++str1;  ++str2;
  561. #if 0
  562.     } else if (*str1 == ' ') {
  563.         ++str1;
  564.     } else if (*str2 == ' ') {
  565.         ++str2;
  566. #endif
  567.     } else {
  568.         return *str1 - *str2;
  569.     }
  570.     }
  571.     if (*str1 == '\0') {
  572.     if (*str2 == '\0') {
  573.         return 0;
  574.     } else {
  575.         return 1;
  576.     }
  577.     } else {
  578.     if (*str2 == '\0') {
  579.         return -1;
  580.     } else {
  581.         /* can never happen, but humor the compiler */
  582.         return 0;
  583.     }
  584.     }
  585. }
  586.  
  587.  
  588. /* Try to find the best of multiple images for the given bounding box. */
  589. /* Don't return anything that won't fit in min space. */
  590.  
  591. Image *
  592. best_image(imf, w, h)
  593. ImageFamily *imf;
  594. int w, h;
  595. {
  596.     Image *img, *best = NULL, *fallback = NULL;
  597.  
  598.     if (imf == NULL)
  599.       return NULL;
  600.     for (img = imf->images; img != NULL; img = img->next) {
  601.     /* Exact matches need no further searching. */
  602.     if (w == img->w && h == img->h && right_depth(img)) {
  603.         return img;
  604.     } else if (between(img->minw, w, img->maxw)
  605.            && between(img->minh, h, img->maxh)) {
  606.         /* Image is plausible - go for the largest one that will fit. */
  607.         if (!best || (img->w > best->w && img->h > best->h))
  608.           best = img;
  609.     } else if (w >= img->minw && h >= img->minh) {
  610.         /* Image is not really big enough, but keep as a fallback. */
  611.         /* Prefer the largest fallback possible. */
  612.         if (!fallback || (img->w > fallback->w && img->h > fallback->h))
  613.           fallback = img;
  614.     } else if (img->istile) {
  615.         /* Image is not really appropriate, but keep as a fallback. */
  616.         /* Prefer the largest fallback possible. */
  617.         if (!fallback || (img->w > fallback->w && img->h > fallback->h))
  618.           fallback = img;
  619.     }
  620.     }
  621.     return (best ? best : fallback);
  622. }
  623.  
  624. int
  625. right_depth(img)
  626. Image *img;
  627. {
  628.     return TRUE;
  629. }
  630.  
  631. /* The comparison function for the image list just does "strcmp" order
  632.    and *requires* that all image families be named and named uniquely. */
  633.  
  634. static int
  635. image_name_compare(im1, im2)
  636. #ifdef THINK_C
  637. const
  638. #endif
  639. void *im1, *im2;
  640. {
  641.     return strcmp((*((ImageFamily **) im1))->name,
  642.           (*((ImageFamily **) im2))->name);
  643. }
  644.  
  645. void
  646. sort_all_images()
  647. {
  648.     qsort(&(images[0]), numimages, sizeof(ImageFamily *), image_name_compare);
  649. }
  650.  
  651. static int
  652. color_name_compare(im1, im2)
  653. #ifdef THINK_C
  654. const
  655. #endif
  656. void *im1, *im2;
  657. {
  658.     return strcmp((*((ImageColor **) im1))->name,
  659.           (*((ImageColor **) im2))->name);
  660. }
  661.  
  662. void
  663. sort_all_colors()
  664. {
  665.     qsort(&(colors[0]), numcolors, sizeof(ImageColor *),  color_name_compare);
  666. }
  667.  
  668. /* Write out the entire image family. */
  669.  
  670. void
  671. write_imf(fp, imf)
  672. FILE *fp;
  673. ImageFamily *imf;
  674. {
  675.     int w, h;
  676.     Image *img;
  677.     
  678.     if (imf == NULL)
  679.       return;
  680.     if (imf->name == NULL) {
  681.     fprintf(fp, "; garbage image family?\n");
  682.     return;
  683.     }
  684.     for (img = imf->images; img != NULL; img = img->next) {
  685.     if (img->monodata != lispnil
  686.         || img->maskdata != lispnil
  687.         || img->colrdata != lispnil
  688.         || img->rawmonodata != NULL
  689.         || img->rawmaskdata != NULL
  690.         || img->rawcolrdata != NULL) {
  691.         fprintf(fp, "(imf \"%s\"", imf->name);
  692.         fprintf(fp, " (");
  693.         fprintf(fp, "(%d %d", img->w, img->h);
  694.         if (img->istile) fprintf(fp, " tile");
  695.         fprintf(fp, ")");
  696.         if (img->embedname) {
  697.         fprintf(fp, " (embed \"%s\")", img->embedname);
  698.         }
  699.         /* (should write embedrect if defined, etc) */
  700.         if (img->colrdata != lispnil || img->rawcolrdata) {
  701.         fprintf(fp, "\n  ");
  702.         write_pixmap(fp, img->w, img->h, img->actualw, img->actualh,
  703.                  img->pixelsize, img->rowbytes,
  704.                  img->palette, img->rawpalette, img->numcolors,
  705.                  img->colrdata, img->rawcolrdata);
  706.         }
  707.         if (img->monodata != lispnil || img->rawmonodata) {
  708.         fprintf(fp, "\n  ");
  709.         write_bitmap(fp, "mono", img->w, img->h,
  710.                  img->monodata, img->rawmonodata);
  711.         }
  712.         if (img->maskdata != lispnil || img->rawmaskdata) {
  713.         fprintf(fp, "\n  ");
  714.         write_bitmap(fp, "mask", img->w, img->h,
  715.                  img->maskdata, img->rawmaskdata);
  716.         }
  717.         fprintf(fp, "))\n");
  718.     }
  719.     }
  720. }
  721.  
  722. static void
  723. write_pixmap(fp, w, h, actualw, actualh, pixelsize, rowbytes,
  724.          palette, rawpalette, numcolors, lispdata, rawdata)
  725. FILE *fp;
  726. int w, h, actualw, actualh, pixelsize, rowbytes, numcolors;
  727. Obj *palette, *lispdata;
  728. int *rawpalette;
  729. char *rawdata;
  730. {
  731.     int dolisp, i, j = 0, numbytes, byte, jump = 0;
  732.     char *datastr = NULL;
  733.     
  734.     actualw = (actualw != 0 ? actualw : w);
  735.     actualh = (actualh != 0 ? actualh : h);
  736.     dolisp = (lispdata != lispnil);    
  737.     numbytes = actualh * rowbytes;
  738.     fprintf(fp, "(color");
  739.     if (actualw != w || actualh != h)
  740.       fprintf(fp, " (actual %d %d)", actualw, actualh);
  741.     fprintf(fp, " (pixel-size %d)", pixelsize);
  742.     fprintf(fp, " (row-bytes %d)", rowbytes);
  743.     if (palette != lispnil) {
  744.     write_palette_contents(fp, palette);
  745.     } else if (rawpalette && numcolors) {
  746.      fprintf(fp, "    (palette");
  747.      for (i = 0; i < numcolors; i++) {
  748.          fprintf(fp, "\n      (%d %d %d %d)",
  749.              rawpalette[4*i],   rawpalette[4*i+1],
  750.              rawpalette[4*i+2], rawpalette[4*i+3]);
  751.      }
  752.      fprintf(fp, ")");
  753.     }
  754.     fprintf(fp, "\n   \"");
  755.     for (i = 0; i < numbytes; ++i) {
  756.     if (i > 0 && i % 32 == 0)
  757.       fprintf(fp, "\"\n   \"");
  758.     if (i > 0 && i % 32 != 0 && i % rowbytes == 0)
  759.       fprintf(fp, "/");
  760.     if (dolisp) {
  761.         if (datastr == NULL || datastr[j] == '\0') {
  762.         if (!stringp(car(lispdata)))
  763.           break;
  764.         datastr = c_string(car(lispdata));
  765.         j = 0;
  766.         lispdata = cdr(lispdata);
  767.         }
  768.         if (datastr[j] == '/')
  769.           ++j;
  770.         byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
  771.         if (jump == 1 || (jump > 0 && i % jump == 0))
  772.           i += jump;
  773.         j += 2;
  774.     } else {
  775.         byte = rawdata[i];
  776.     }
  777.     fprintf(fp, "%02x", (unsigned char) byte);
  778.     }
  779.     fprintf(fp, "\")");
  780. }
  781.  
  782. static void
  783. write_bitmap(fp, subtyp, w, h, lispdata, rawdata)
  784. FILE *fp;
  785. char *subtyp;
  786. int w, h;
  787. Obj *lispdata;
  788. char *rawdata;
  789. {
  790.     int dolisp, i, j = 0, rowbytes, numbytes, byte, jump = 0;
  791.     char *datastr = NULL;
  792.     
  793.     dolisp = (lispdata != lispnil);    
  794.     rowbytes = (w + 7) / 8;
  795.     numbytes =  h * rowbytes;
  796.     fprintf(fp, "(%s", subtyp);
  797.     if (w > 16 || h > 16)
  798.       fprintf(fp, "\n  ");
  799.     fprintf(fp, " \"");
  800.     for (i = 0; i < numbytes; ++i) {
  801.     if (i > 0 && i % 32 == 0)
  802.       fprintf(fp, "\"\n   \"");
  803.     if (i > 0 && i % 32 != 0 && i % rowbytes == 0)
  804.       fprintf(fp, "/");
  805.     if (dolisp) {
  806.         if (datastr == NULL || datastr[j] == '\0') {
  807.         if (!stringp(car(lispdata)))
  808.           break;
  809.         datastr = c_string(car(lispdata));
  810.         j = 0;
  811.         lispdata = cdr(lispdata);
  812.         }
  813.         if (datastr[j] == '/')
  814.           ++j;
  815.         byte = hextoi(datastr[j]) * 16 + hextoi(datastr[j+1]);
  816.         if (jump == 1 || (jump > 0 && i % jump == 0))
  817.           i += jump;
  818.         j += 2;
  819.     } else {
  820.         byte = rawdata[i];
  821.     }
  822.     fprintf(fp, "%02x", (unsigned char) byte);
  823.     }
  824.     fprintf(fp, "\")");
  825. }
  826.  
  827. static void
  828. write_palette_contents(fp, palette)
  829. FILE *fp;
  830. Obj *palette;
  831. {
  832.     int len;
  833.     Obj *color;
  834.  
  835.     if (palette != lispnil) {
  836.         len = length(palette);
  837.     if (len > 2)
  838.       fprintf(fp, "\n  ");
  839.     fprintf(fp, " (palette");
  840.     for (; palette != lispnil; palette = cdr(palette)) {
  841.         color = car(palette);
  842.         if (len > 2)
  843.           fprintf(fp, "\n   ");
  844.         fprintf(fp, " (%d %d %d %d)",
  845.             c_number(car(color)),
  846.             c_number(car(cdr(color))),
  847.             c_number(car(cdr(cdr(color)))),
  848.             c_number(car(cdr(cdr(cdr(color))))));
  849.     }
  850.     fprintf(fp, ")");
  851.     }
  852. }
  853.  
  854. void
  855. write_imc(fp, imc)
  856. FILE *fp;
  857. ImageColor *imc;
  858. {
  859.     if (imc == NULL)
  860.       return;
  861.     if (imc->name == NULL) {
  862.     fprintf(fp, "; garbage color?\n");
  863.     return;
  864.     }
  865.     fprintf(fp, "(color \"%s\" %d %d %d)\n",
  866.         imc->name, imc->r, imc->g, imc->b);
  867. }
  868.